package com.lry.thompson;

import com.lry.input.Input;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class MiniDfaMachine {

    private DfaMachine dfaMachine;

    private int[][] minDfa; //一维节点经过二维字符到达数组值那个节点  [0]['a'] = 1, 0节点经过字符a到达节点1

    private int[][] dfaTransTable;

    boolean addNewGroup = true;

    private MiniDfa newGroup = null;

    public MiniDfaMachine(DfaMachine dfaMachine){
        this.dfaMachine = dfaMachine;
        this.dfaTransTable = dfaMachine.getDfaStateTransformTable();
    }

    public void minimize() {
        System.out.println("\nsimplefier dfa");
        generateTwoGroup();

        while(addNewGroup){
            addNewGroup = false;
            doGroupSeperationOnNumber();
            doGroupSeperationOnCharacter();
        }

        createMiniDfaTransTable();
        printSimpleMiniDfaTable();
    }


    private void printSimpleMiniDfaTable() {
        for (int i = 0; i < MiniDfaManager.getMiniDfas().size(); i++){
            for (int j = 0; j < MiniDfaManager.getMiniDfas().size(); j++) {
                if (isOnLowCharacterClass(i,j)) {
                    System.out.println("from " + i + " to " + j + " on a");
                }
                if (isOnHighCharacterClass(i,j)) {
                    System.out.println("from " + i + " to " + j + " on A");
                }
                if (isOnNumberClass(i,j)) {
                    System.out.println("from " + i + " to " + j + " on D");
                }
                if (isOnDot(i,j)) {
                    System.out.println("from " + i + " to " + j + " on .");
                }
                if (isOnDash(i,j)) {
                    System.out.println("from " + i + " to " + j + " on _");
                }
            }
        }
    }

    private boolean isOnLowCharacterClass(int from, int to) {
        for (char c = 'a'; c <= 'z'; c++) {
            if (minDfa[from][c] != to) {
                return false;
            }
        }
        return true;
    }

    private boolean isOnHighCharacterClass(int from, int to) {
        for (char c = 'A'; c <= 'Z'; c++) {
            if (minDfa[from][c] != to) {
                return false;
            }
        }
        return true;
    }
    private boolean isOnNumberClass(int from, int to) {
        char c = '0';
        for (c = '0'; c <= '9'; c++) {
            if (minDfa[from][c] != to) {
                return false;
            }
        }
        return true;
    }

    private boolean isOnDot(int from, int to) {
        if (minDfa[from]['.'] != to) {
            return false;
        }
        return true;
    }

    private boolean isOnDash(int from, int to) {
        if (minDfa[from]['_'] != to) {
            return false;
        }
        return true;
    }

    public int yy_next(int state, byte yylook) {
        if (state == DfaMachine.STATE_FAILURE || yylook >= DfaMachine.ASCII_COUNT) {
            return DfaMachine.STATE_FAILURE;
        }
        return minDfa[state][yylook];
    }

    public void interpreter() {
        System.out.println("miniDfa Input string: ");
        dfaMachine.getInput().renewInputBuffer();

        List<String> inputList = dfaMachine.getInput().readInput();

        for(String str:inputList){
            MiniDfa miniDfa = MiniDfaManager.start;
            int groupNum = miniDfa.groupNum;

            byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
            for (byte b : bytes) {
                groupNum = yy_next(groupNum,b);
            }
            if(groupNum==DfaMachine.STATE_FAILURE){
                System.out.println("The MINI DFA Machine can not recognize string: " + str);
            }else{
                if(MiniDfaManager.isEnd(groupNum)){
                    System.out.println("The MINI DFA Machine can recognize string: " + str);
                }else{
                    System.out.println("The MINI DFA Machine can not recognize string: " + str);
                }
            }
        }
    }
    private void createMiniDfaTransTable() {
        initMiniDfaTransTable();

        for (Dfa dfa : DfaManager.DFA_2_STATE_NUM.keySet()) {
            int from = dfa.stateNum;
            for (int i = 0; i < DfaMachine.ASCII_COUNT; i++) {
                if (dfaTransTable[from][i] != DfaMachine.STATE_FAILURE) {
                    int to = dfaTransTable[from][i];

                    MiniDfa fromGroup = MiniDfaManager.getByDfaStateNum(from);
                    MiniDfa toGroup = MiniDfaManager.getByDfaStateNum(to);
                    minDfa[fromGroup.groupNum][i] = toGroup.groupNum;
                }
            }
        }
    }

    private void initMiniDfaTransTable() {
        minDfa = new int[MiniDfaManager.getMiniDfas().size()][DfaMachine.ASCII_COUNT];
        for (int i = 0; i <MiniDfaManager.getMiniDfas().size(); i++){
            for (int j = 0; j < DfaMachine.ASCII_COUNT; j++) {
                minDfa[i][j] = DfaMachine.STATE_FAILURE;
            }
        }
    }

    private void doGroupSeperationOnCharacter() {
        for (int i =0;i<MiniDfaManager.getMiniDfas().size(); i++) {
            MiniDfa miniDfa = MiniDfaManager.getMiniDfas().get(i);
            int dfaCount = 1;
            Dfa first = miniDfa.getDfa(0);
            Dfa next = miniDfa.getDfa(dfaCount);
            newGroup = null;

            while(null!=next){
                for (char c = 0; c < DfaMachine.ASCII_COUNT; c++) {
                    if (!Character.isDigit(c) && doGroupSeprationOnInput(miniDfa, first, next, c)) {
                        addNewGroup = true;
                        break;
                    }
                }
                dfaCount++;
                next = miniDfa.getDfa(dfaCount);
            }

            miniDfa.commitRemove();
        }
    }

    private void doGroupSeperationOnNumber() {
        for (int i =0;i<MiniDfaManager.getMiniDfas().size(); i++) {
            MiniDfa miniDfa = MiniDfaManager.getMiniDfas().get(i);
            int dfaCount = 1;
            Dfa first = miniDfa.getDfa(0);
            Dfa next = miniDfa.getDfa(dfaCount);
            newGroup = null;

            while(null!=next){
                for (char c = '0'; c <= '9'; c++) {
                    if (doGroupSeprationOnInput(miniDfa, first, next, c)) {
                        addNewGroup = true;
                        break;
                    }
                }
                dfaCount++;
                next = miniDfa.getDfa(dfaCount);
            }

            miniDfa.commitRemove();
        }
    }

    /**
     * 判断first和next节点遇到字符c 分别跳转到哪个分区，如果分区不同，则需要将两个节点划分
     * @param miniDfa
     * @param first
     * @param next
     * @param c
     * @return
     */
    private boolean doGroupSeprationOnInput(MiniDfa miniDfa, Dfa first, Dfa next, char c) {
        int goto_first = dfaTransTable[first.stateNum][c];//跳转到另一个节点的dfa stateNum
        int goto_next = dfaTransTable[next.stateNum][c];

        if(MiniDfaManager.getByDfaStateNum(goto_first)
                !=MiniDfaManager.getByDfaStateNum(goto_next)){

            if (newGroup == null) {
                newGroup = MiniDfaManager.createMiniDfa();
            }
            //把next从旧分组移除加入新分组
            miniDfa.tobeRemove(next);
            newGroup.addDfa(next);
            return true;
        }
        return false;
    }

    //分为终态集和非终态集
    private void generateTwoGroup() {
        List<Dfa> acceptedDfaList = new ArrayList<>();
        List<Dfa> dfaList = new ArrayList<>();

        for (Dfa dfa : DfaManager.DFA_2_STATE_NUM.keySet()) {
            if(dfa.accepted){
                acceptedDfaList.add(dfa);
            }else {
                dfaList.add(dfa);
            }
        }

        if(acceptedDfaList.size()>0){
            MiniDfa acceptedMiniDfa = MiniDfaManager.createMiniDfa();
            for (Dfa dfa : acceptedDfaList) {
                acceptedMiniDfa.addDfa(dfa);
            }
        }
        if(dfaList.size()>0){
            MiniDfa miniDfa = MiniDfaManager.createMiniDfa();
            for (Dfa dfa : dfaList) {
                miniDfa.addDfa(dfa);
            }
        }
    }

}
